

#import "NSData+extension.h"
static const char _base64EncodingTable[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const short _base64DecodingTable[256] = {
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -1, -1, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, -2, -2, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -2, -2, -2,
    -2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, -2,
    -2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
    -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};


@implementation NSData (extension)
- (NSString *)md2String {
    unsigned char result[CC_MD2_DIGEST_LENGTH];
    CC_MD2(self.bytes, (CC_LONG)self.length, result);
    return [NSString stringWithFormat:
            @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]
            ];
}

- (NSString *)md4String {
    unsigned char result[CC_MD4_DIGEST_LENGTH];
    CC_MD4(self.bytes, (CC_LONG)self.length, result);
    return [NSString stringWithFormat:
            @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]
            ];
}

- (NSString *)md5String {
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(self.bytes, (CC_LONG)self.length, result);
    return [NSString stringWithFormat:
            @"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
            result[0], result[1], result[2], result[3],
            result[4], result[5], result[6], result[7],
            result[8], result[9], result[10], result[11],
            result[12], result[13], result[14], result[15]
            ];
}

- (NSData *)md2Data {
    unsigned char result[CC_MD2_DIGEST_LENGTH];
    CC_MD2(self.bytes, (CC_LONG)self.length, result);
    return [NSData dataWithBytes:result length:CC_MD2_DIGEST_LENGTH];
}

- (NSData *)md4Data {
    unsigned char result[CC_MD4_DIGEST_LENGTH];
    CC_MD4(self.bytes, (CC_LONG)self.length, result);
    return [NSData dataWithBytes:result length:CC_MD4_DIGEST_LENGTH];
}

- (NSData *)md5Data {
    unsigned char result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(self.bytes, (CC_LONG)self.length, result);
    return [NSData dataWithBytes:result length:CC_MD5_DIGEST_LENGTH];
}

//sha
- (NSString *)sha1String {
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(self.bytes, (CC_LONG)self.length, digest);
    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
    for(int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    return output;
}

- (NSString *)sha224String {
    uint8_t digest[CC_SHA224_DIGEST_LENGTH];
    CC_SHA224(self.bytes, (CC_LONG)self.length, digest);
    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA224_DIGEST_LENGTH * 2];
    for(int i = 0; i < CC_SHA224_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    return output;
}

- (NSString *)sha256String {
    uint8_t digest[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256(self.bytes, (CC_LONG)self.length, digest);
    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA256_DIGEST_LENGTH * 2];
    for(int i = 0; i < CC_SHA256_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    return output;
}

- (NSString *)sha384String {
    uint8_t digest[CC_SHA384_DIGEST_LENGTH];
    CC_SHA384(self.bytes, (CC_LONG)self.length, digest);
    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA384_DIGEST_LENGTH * 2];
    for(int i = 0; i < CC_SHA384_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    return output;
}

- (NSString *)sha512String {
    uint8_t digest[CC_SHA512_DIGEST_LENGTH];
    CC_SHA512(self.bytes, (CC_LONG)self.length, digest);
    NSMutableString* output = [NSMutableString stringWithCapacity:CC_SHA512_DIGEST_LENGTH * 2];
    for(int i = 0; i < CC_SHA512_DIGEST_LENGTH; i++)
        [output appendFormat:@"%02x", digest[i]];
    return output;
}

- (NSData *)sha1Data {
    uint8_t digest[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(self.bytes, (CC_LONG)self.length, digest);
    return [NSData dataWithBytes:digest length:CC_SHA1_DIGEST_LENGTH];
}

- (NSData *)sha224Data {
    uint8_t digest[CC_SHA224_DIGEST_LENGTH];
    CC_SHA224(self.bytes, (CC_LONG)self.length, digest);
    return [NSData dataWithBytes:digest length:CC_SHA224_DIGEST_LENGTH];
}

- (NSData *)sha256Data {
    uint8_t digest[CC_SHA256_DIGEST_LENGTH];
    CC_SHA256(self.bytes, (CC_LONG)self.length, digest);
    return [NSData dataWithBytes:digest length:CC_SHA256_DIGEST_LENGTH];
}

- (NSData *)sha384Data {
    uint8_t digest[CC_SHA384_DIGEST_LENGTH];
    CC_SHA384(self.bytes, (CC_LONG)self.length, digest);
    return [NSData dataWithBytes:digest length:CC_SHA384_DIGEST_LENGTH];
}

- (NSData *)sha512Data {
    uint8_t digest[CC_SHA512_DIGEST_LENGTH];
    CC_SHA512(self.bytes, (CC_LONG)self.length, digest);
    return [NSData dataWithBytes:digest length:CC_SHA512_DIGEST_LENGTH];
}

// mac sha secret
- (NSData *)hmacDataAlg:(CCHmacAlgorithm)alg secret:(NSString *)key {
    size_t size = 0;
    switch (alg) {
        case kCCHmacAlgSHA1: size = CC_SHA1_DIGEST_LENGTH; break;
        case kCCHmacAlgMD5: size = CC_MD5_DIGEST_LENGTH; break;
        case kCCHmacAlgSHA256: size = CC_SHA256_DIGEST_LENGTH; break;
        case kCCHmacAlgSHA384: size = CC_SHA384_DIGEST_LENGTH; break;
        case kCCHmacAlgSHA512: size = CC_SHA512_DIGEST_LENGTH; break;
        case kCCHmacAlgSHA224: size = CC_SHA224_DIGEST_LENGTH; break;
        default:
            break;
    }
    const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
    uint8_t result[size];
    CCHmac(alg, cKey, strlen(cKey), self.bytes, self.length, result);
    return [NSData dataWithBytes:result length:size];
}

- (NSString *)hmacStringAlg:(CCHmacAlgorithm)alg scecret:(NSString *)key {
    size_t size = 0;
    switch (alg) {
        case kCCHmacAlgSHA1: size = CC_SHA1_DIGEST_LENGTH; break;
        case kCCHmacAlgMD5: size = CC_MD5_DIGEST_LENGTH; break;
        case kCCHmacAlgSHA256: size = CC_SHA256_DIGEST_LENGTH; break;
        case kCCHmacAlgSHA384: size = CC_SHA384_DIGEST_LENGTH; break;
        case kCCHmacAlgSHA512: size = CC_SHA512_DIGEST_LENGTH; break;
        case kCCHmacAlgSHA224: size = CC_SHA224_DIGEST_LENGTH; break;
        default:
            break;
    }
    const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
    uint8_t result[size];
    CCHmac(alg, cKey, strlen(cKey), self.bytes, self.length, result);
    // string -- data
    NSMutableString *hash = [NSMutableString stringWithCapacity:size * 2];
    for (int i = 0; i < size; i++) {
        [hash appendFormat:@"%02x", result[i]];
    }
    return hash;
}

- (NSData *)hmacSha1DataWithKey:(NSString *)key {
    return [self hmacDataAlg:kCCHmacAlgSHA1 secret:key];
}
- (NSData *)hmacMD5DataWithKey:(NSString *)key {
    return [self hmacDataAlg:kCCHmacAlgMD5 secret:key];
}
- (NSData *)hmacSha256DataWithKey:(NSString *)key {
    return [self hmacDataAlg:kCCHmacAlgSHA256 secret:key];
}
- (NSData *)hmacSha384DataWithKey:(NSString *)key {
    return [self hmacDataAlg:kCCHmacAlgSHA384 secret:key];
}
- (NSData *)hmacSha512DataWithKey:(NSString *)key {
    return [self hmacDataAlg:kCCHmacAlgSHA512 secret:key];
}
- (NSData *)hmacSha224DataWithKey:(NSString *)key {
    return [self hmacDataAlg:kCCHmacAlgSHA224 secret:key];
}

// base64
+ (NSData *)decodeBase64WithString:(NSString *)strBase64 {
    const char * objPointer = [strBase64 cStringUsingEncoding:NSUTF8StringEncoding];
    int intLength = (int)strlen(objPointer);
    int intCurrent;
    int i = 0, j = 0, k;
    
    unsigned char * objResult;
    objResult = (unsigned char *)calloc(intLength, sizeof(unsigned char));
    
    // Run through the whole string, converting as we go
    while ( ((intCurrent = *objPointer++) != '\0') && (intLength-- > 0) ) {
        if (intCurrent == '=') {
            if (*objPointer != '=' && ((i % 4) == 1)) {// || (intLength > 0)) {
                // the padding character is invalid at this point -- so this entire string is invalid
                free(objResult);
                return nil;
            }
            continue;
        }
        
        intCurrent = _base64DecodingTable[intCurrent];
        if (intCurrent == -1) {
            // we're at a whitespace -- simply skip over
            continue;
        } else if (intCurrent == -2) {
            // we're at an invalid character
            free(objResult);
            return nil;
        }
        
        switch (i % 4) {
            case 0:
                objResult[j] = intCurrent << 2;
                break;
                
            case 1:
                objResult[j++] |= intCurrent >> 4;
                objResult[j] = (intCurrent & 0x0f) << 4;
                break;
                
            case 2:
                objResult[j++] |= intCurrent >>2;
                objResult[j] = (intCurrent & 0x03) << 6;
                break;
                
            case 3:
                objResult[j++] |= intCurrent;
                break;
        }
        i++;
    }
    
    // mop things up if we ended on a boundary
    k = j;
    if (intCurrent == '=') {
        switch (i % 4) {
            case 1:
                // Invalid state
                free(objResult);
                return nil;
                
            case 2:
                k++;
                // flow through
            case 3:
                objResult[k] = 0;
        }
    }
    
    // Cleanup and setup the return NSData
    NSData * objData = [[NSData alloc] initWithBytes:objResult length:j];
    free(objResult);
    return objData;
}


- (NSString *)encodeBase64{
    const unsigned char * objRawData = (const unsigned char *)[self bytes];
    char * objPointer;
    char * strResult;
    // Get the Raw Data length and ensure we actually have data
    int intLength = (int)[self length];
    if (intLength == 0) return nil;
    // Setup the String-based Result placeholder and pointer within that placeholder
    strResult = (char *)calloc(((intLength + 2) / 3) * 4, sizeof(char));
    objPointer = strResult;
    
    // Iterate through everything
    while (intLength > 2) { // keep going until we have less than 24 bits
        *objPointer++ = _base64EncodingTable[objRawData[0] >> 2];
        *objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) << 4) + (objRawData[1] >> 4)];
        *objPointer++ = _base64EncodingTable[((objRawData[1] & 0x0f) << 2) + (objRawData[2] >> 6)];
        *objPointer++ = _base64EncodingTable[objRawData[2] & 0x3f];
        
        // we just handled 3 octets (24 bits) of data
        objRawData += 3;
        intLength -= 3;
    }
    
    // now deal with the tail end of things
    if (intLength != 0) {
        *objPointer++ = _base64EncodingTable[objRawData[0] >> 2];
        if (intLength > 1) {
            *objPointer++ = _base64EncodingTable[((objRawData[0] & 0x03) << 4) + (objRawData[1] >> 4)];
            *objPointer++ = _base64EncodingTable[(objRawData[1] & 0x0f) << 2];
            *objPointer++ = '=';
        } else {
            *objPointer++ = _base64EncodingTable[(objRawData[0] & 0x03) << 4];
            *objPointer++ = '=';
            *objPointer++ = '=';
        }
    }
    
    // Terminate the string-based result
    *objPointer = '\0';
    
    // Return the results as an NSString object
    NSString *retStr = [NSString stringWithCString:strResult encoding:NSUTF8StringEncoding];
    
    free(strResult);
    return retStr;
}

// des aes
+ (NSData *)doCipher:(NSData *)sTextIn
                 key:(NSData *)sKey
           Algorithm:(CCAlgorithm)algorithm
             context:(CCOperation)encryptOrDecrypt {
    
    NSData * dTextIn;
    dTextIn = [sTextIn mutableCopy];
    NSMutableData *dKey = [sKey mutableCopy];
    int moreSize = 0;
    
    //make key to standard;
    switch (algorithm) {
        case kCCAlgorithmDES:
            moreSize = kCCBlockSizeDES;
            [dKey setLength:kCCKeySizeDES];
            break;
        case kCCAlgorithm3DES:
            moreSize = kCCBlockSize3DES;
            [dKey setLength:kCCKeySize3DES];
            break;
        case kCCAlgorithmAES128:
            moreSize = kCCBlockSizeAES128;
            [dKey setLength:kCCKeySizeAES128];
            break;
        case kCCAlgorithmCAST:
            moreSize = kCCBlockSizeCAST;
            [dKey setLength:kCCKeySizeMaxCAST];
            break;
        case kCCAlgorithmRC4:
        case kCCAlgorithmRC2:
            moreSize = kCCBlockSizeRC2;
            [dKey setLength:kCCKeySizeMaxRC2];
            break;
        default:
            return nil;
            break;
    }
    
    uint8_t *bufferPtr1 = NULL;
    size_t bufferPtrSize1 = 0;
    size_t movedBytes1 = 0;
    
    unsigned char iv[8];
    memset(iv, 0, 8);
    
    bufferPtrSize1 = [sTextIn length] + moreSize;
    bufferPtr1 = (uint8_t *)malloc(bufferPtrSize1);
    memset((void *)bufferPtr1, 0, bufferPtrSize1);
    NSLog(@"%@", [[NSString alloc]initWithData:dKey encoding:NSUTF8StringEncoding]);
    // cryption....
    CCCryptorStatus ccStatus = CCCrypt(encryptOrDecrypt, // CCOperation op
                                       algorithm, // CCAlgorithm alg
                                       kCCOptionPKCS7Padding|kCCOptionECBMode, // CCOptions options
                                       [dKey bytes], // const void *key
                                       [dKey length], // size_t keyLength
                                       iv, // const void *iv
                                       [dTextIn bytes], // const void *dataIn
                                       [dTextIn length], // size_t dataInLength
                                       (void *)bufferPtr1, // void *dataOut
                                       bufferPtrSize1, // size_t dataOutAvailable
                                       &movedBytes1); // size_t *dataOutMoved
    
    // output situation after crypt
    switch (ccStatus) {
        case kCCSuccess:
            NSLog(@"CRYPTION SUCCESS");
            break;
        case kCCParamError:
            NSLog(@"CRYPTION PARAM ERROR");
            break;
        case kCCBufferTooSmall:
            NSLog(@"CRYPTION BUFFER TOO SMALL");
            break;
        case kCCMemoryFailure:
            NSLog(@"CRYPTION MEMORY FAILURE");
            break;
        case kCCAlignmentError:
            NSLog(@"CRYPTION ALIGNMENT ERROR");
            break;
        case kCCDecodeError:
            NSLog(@"CRYPTION DECODE ERROR");
            break;
        case kCCUnimplemented:
            NSLog(@"CRYPTION UNIMPLEMENTED");
            break;
        default:
            break;
    }
    
    if (ccStatus == kCCSuccess){
        NSData *result = [NSData dataWithBytes:bufferPtr1 length:movedBytes1];
        free(bufferPtr1);
        return result;
    }
    free(bufferPtr1);
    return nil;
}

+ (NSData*)doCipherUse3DesMethod:(NSData *)sTextIn
                             key:(NSData *)sKey
                         context:(CCOperation)encryptOrDecrypt{
    return [self doCipher:sTextIn
                      key:sKey
                Algorithm:kCCAlgorithm3DES
                  context:encryptOrDecrypt];
}

+ (NSData *) doCipherUseCastMethod:(NSData *)sTextIn
                               key:(NSData *)sKey
                           context:(CCOperation)encryptOrDecrypt{
    
    return [self doCipher:sTextIn
                      key:sKey
                Algorithm:kCCAlgorithmCAST
                  context:encryptOrDecrypt];
    
}

+ (NSData*)doCipherUseDesMethod:(NSData *)sTextIn
                            key:(NSData *)sKey
                        context:(CCOperation)encryptOrDecrypt{
    return [self doCipher:sTextIn
                      key:sKey
                Algorithm:kCCAlgorithmDES
                  context:encryptOrDecrypt];
}

+ (NSData*)doCipherUseAes128Method:(NSData *)sTextIn
                               key:(NSData *)sKey
                           context:(CCOperation)encryptOrDecrypt{
    return [self doCipher:sTextIn
                      key:sKey
                Algorithm:kCCAlgorithmAES128
                  context:encryptOrDecrypt];
}

+ (NSData*)doCipherUseAesMethod:(NSData *)sTextIn
                            key:(NSData *)sKey
                        context:(CCOperation)encryptOrDecrypt{
    return [self doCipher:sTextIn
                      key:sKey
                Algorithm:kCCAlgorithmAES
                  context:encryptOrDecrypt];
}
@end
